# -*- coding: UTF-8 -*-
import functools
import traceback
from PIL import Image, ImageTk
from Code.Utils.sc_func import Common, Singleton
from Code.Binder.sc_template import DataTemplate, DataCreater
from Code.Model.sc_global import Global
from Code.Model.Controller.sc_logger import logger


class SettingsDataError(Exception):
    pass


class DependenceDataError(Exception):
    pass


class LanguageDataError(Exception):
    pass


class PlotDataError(Exception):
    pass


class TaskDataError(Exception):
    pass


class AppTitleData(DataTemplate):
    pass


class AppTreesData(DataTemplate):
    pass


class AppWidgetsData(DataTemplate):
    pass


class ExceptionData(DataTemplate):
    pass


class TryLoginReturnData(DataTemplate):
    """ 单个IP尝试登录的结果数据 """
    pass


class NewLoginReturnData(DataTemplate):
    """ 新建登录的结果数据 """
    pass


class LoginStateData(DataTemplate):
    """ 所有IP登录状态数据 """
    pass


class UpdateInfoTextData(DataTemplate):
    pass


class ServerCacheData(DataTemplate):
    def __init__(self):
        self.init({})


class UpdateProgressData(DataTemplate):
    pass


class UpdatePrintoutData(DataTemplate):
    pass


class UpdateWidgetsData(DataTemplate):
    pass


class DelayLoopTimerData(DataTemplate):
    pass


class SingleStartResultData(DataTemplate):
    pass


class SingleUploadResultData(DataTemplate):
    pass


class SingleDownloadResultData(DataTemplate):
    pass


def wrapper_exception(func):
    """ 异常捕获, 此方法只包装异常, 完善异常信息 """

    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except SettingsDataError as e:
            err = 'settings.json {}:\n{}'.format(model_gate.query_language_data('DATA', 'EXCEPTION'), e)
        except DependenceDataError as e:
            err = 'dependence.json {}:\n{}'.format(model_gate.query_language_data('DATA', 'EXCEPTION'), e)
        except LanguageDataError as e:
            err = 'language.json {}:\n{}'.format(model_gate.query_language_data('DATA', 'EXCEPTION'), e)
        except PlotDataError as e:
            err = 'PlotTimer {}:\n{}'.format(model_gate.query_language_data('DATA', 'EXCEPTION'), e)
        except TaskDataError as e:
            err = '{}:\n{}'.format(model_gate.query_language_data('TASK', 'DATA', 'EXCEPTION'), e)
        except FileNotFoundError as e:
            err = str(e)
        except Exception:
            err = 'Unknown Exception:\n{}'.format(traceback.format_exc())

        logger.error(err)
        model_gate.exception_data.set_data(err)   # , asynccall=True)
        model_gate.update_infotext_data.set_data((err.replace('\n', ''), False))   # , asynccall=True)

    return wrapper


class Settings(object):
    def __init__(self):
        self.tool_alias = None
        self.tool_version = None
        self.log_level = None
        self.server_dir = None
        self.retry_times = None
        self.login_count_limit = None
        self.keepalive_period = None
        self.refresh_json_period = None
        self.default_passwords = None
        self.about_info = None
        self.pure_mode_switch = None
        self.preference_switch = None
        self.preference_ip = None
        self.sshd_port = None
        self.language_type = ''
        self.auto_plot_period = None
        self.auto_task_period = None
        self.auto_task_scripts = None

    def load(self, settings_data):
        self.settings(settings_data['Settings'])

    def settings(self, global_data):
        self.tool_alias = global_data['tool_alias']
        self.tool_version = global_data['tool_version']
        self.log_level = global_data['log_level']
        self.server_dir = global_data['server_home']
        self.retry_times = global_data['retry_times']
        self.login_count_limit = global_data['login_count_limit']
        self.keepalive_period = global_data['keepalive_period']
        self.refresh_json_period = global_data['refresh_json_period']
        self.about_info = '\n'.join(global_data['about_information'])
        self.pure_mode_switch = global_data['pure_mode_switch']
        self.preference_switch = global_data['preference_switch']
        self.preference_ip = global_data['preference_ip']
        self.sshd_port = global_data['sshd_port']
        self.language_type = global_data['language_type']
        _default_pwd = global_data['passwords']
        self.default_passwords = [_default_pwd['username'], _default_pwd['userpassword'], _default_pwd['rootpassword']]
        self.auto_plot_period = global_data['auto_plot_period']
        self.auto_task_period = global_data['auto_task_period']
        self.auto_task_scripts = global_data['auto_task_scripts']

    def query(self, key):
        if hasattr(self, key):
            return getattr(self, key)
        return None

    def get_about_info(self):
        info = '''                  {}
        
        {}
        版本号:     {}
        作者:       {}
        联系方式:   {}
        官网:       {}'''.format(
            self.tool_alias,
            self.about_info,
            self.tool_version,
            Global.G_AUTHOR_NAME,
            Global.G_CONTACT_ME,
            Global.G_TOOL_WEBSITE)
        return info


class Dependence(object):
    def __init__(self):
        self.photo_images = {}
        self.tree_data = None
        self.widget_data = None

    def load(self, dependence_data, reload):
        # 定时器刷新时不重新加载image数据, 否则界面图标会出问题
        if not reload:
            self.images(dependence_data['Images'])
        self.tree(dependence_data['Trees'])
        self.widgets(dependence_data['Widgets'])

    def images(self, image_data):
        for key, path in image_data.items():
            path = "{}\\{}".format(Global.G_RESOURCE_DIR, path)
            if not Common.is_file(path):
                raise DependenceDataError("%s is not exist !" % path)
            if key == 'ICO':
                self.photo_images[key] = path
                continue
            self.append_photo_image((key, path))

    def append_photo_image(self, data):
        key, path = data
        # 如果这个图片正好还没有生成或者写入完成, Image.open会报异常,
        # 此处稍等一下然后重试 #
        for t in range(5):
            try:
                self.photo_images[key] = ImageTk.PhotoImage(image=Image.open(path))
                return
            except:
                Common.sleep(0.1)
                continue
        raise DependenceDataError('{} to image error, is exist: {}'.format(path, Common.is_file(path)))

    def delete_photo_image(self, data):
        key, = data
        if key in self.photo_images:
            del self.photo_images[key]

    def tree(self, tree_data):
        self.tree_data = tree_data

    def widgets(self, widget_data):
        self.widget_data = widget_data


class Language(object):
    def __init__(self):
        self.lang_data = None

    def load(self, lang_data):
        self.lang_data = lang_data

    def get_types(self):
        return self.lang_data['AA_LANGUAGES']

    def query(self, types, keys):
        infos = ""
        space = '' if types.upper() == 'CHINESE' else ' '
        for key in keys:
            if key in self.lang_data and types in self.lang_data[key]:
                data = self.lang_data[key][types]
            else:
                data = key
            infos = "{}{}{}".format(infos, space, data)
        return infos


class DataInterface:

    all_is_none = [None, "", "null", "NULL"]

    @classmethod
    def get_ssh_instance(cls, ip):
        return model_gate.login_state_data.get_data()[ip]['SSH']

    @classmethod
    def update_infotext_data(cls, ip, task, info, state):
        if info in cls.all_is_none:
            return
        model_gate.update_infotext_data.set_data(("[{:15}] {} {}".format(ip, task, info), state))

    @classmethod
    def update_progress_data(cls, ip, progress, success):
        model_gate.update_progress_data.set_data((ip, progress, success))

    @classmethod
    def update_printout_data(cls, ip, info):
        model_gate.update_printout_data.set_data((ip, info))

    @classmethod
    def update_widgets_data(cls, ip, values):
        model_gate.update_widgets_data.set_data((ip, values))


@Singleton
class ModelGate(object):

    def __init__(self):
        self.settings_data = Settings()
        self.dependence_data = Dependence()
        self.language_data = Language()
        self.app_title_data = DataCreater(AppTitleData)
        self.app_trees_data = DataCreater(AppTreesData)
        self.app_widgets_data = DataCreater(AppWidgetsData)
        self.exception_data = DataCreater(ExceptionData)
        self.try_login_return_data = DataCreater(TryLoginReturnData)
        self.new_login_return_data = DataCreater(NewLoginReturnData)
        self.login_state_data = DataCreater(LoginStateData)
        self.server_cache_data = DataCreater(ServerCacheData)
        self.update_infotext_data = DataCreater(UpdateInfoTextData)
        self.update_progress_data = DataCreater(UpdateProgressData)
        self.update_printout_data = DataCreater(UpdatePrintoutData)
        self.update_widgets_data = DataCreater(UpdateWidgetsData)
        self.delay_loop_timer_data = DataCreater(DelayLoopTimerData)
        self.single_start_result_data = DataCreater(SingleStartResultData)
        self.single_upload_result_data = DataCreater(SingleUploadResultData)
        self.single_download_result_data = DataCreater(SingleDownloadResultData)

    @classmethod
    def query_env_define_data(cls, args):
        if hasattr(Global, args):
            return getattr(Global, args)
        return None

    @classmethod
    def query_running_task_data(cls, args=None):
        return Common.exist_suffix_file(Global.G_PID_DIR, '.lock')[1]

    def query_settings_data(self, args):
        return self.settings_data.query(args)

    def query_app_title_data(self, args=None):
        return '{} v{}'.format(self.settings_data.tool_alias, self.settings_data.tool_version)

    def query_about_info_data(self, args=None):
        return self.settings_data.get_about_info()

    def query_login_limit_data(self, args=None):
        return self.settings_data.login_count_limit

    def query_server_dir_data(self, args=None):
        return self.settings_data.server_dir

    def query_preference_ip_data(self, args=None):
        return self.settings_data.preference_ip

    def query_default_pwd_data(self, args=None):
        return self.settings_data.default_passwords

    def query_photo_image_data(self, args):
        if args not in self.dependence_data.photo_images:
            return None
        return self.dependence_data.photo_images[args]

    def query_tree_view_data(self, args=None):
        return self.dependence_data.tree_data

    def query_widgets_data(self, args=None):
        return self.dependence_data.widget_data

    def query_ips_state_data(self, args=None):
        login_data = self.login_state_data.get_data()
        out = {}
        for ip, item in login_data.items():
            out[ip] = item['STATE']
        return out

    def query_language_types(self, args=None):
        return self.language_data.get_types()

    def query_language_data(self, *args):
        if isinstance(args[0], tuple):
            args = args[0]
        return self.language_data.query(self.settings_data.language_type, args)

    def query_pure_mode_data(self, args=None):
        return True if self.settings_data.pure_mode_switch == 'Yes' else False


model_gate = ModelGate()

